/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openFuyao is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package output

import (
	"encoding/csv"
	"encoding/json"
	"strings"
	"testing"
	"time"

	"env-check/pkg/config"
)

// createTestQueryResult 创建测试查询结果
func createTestQueryResult() *config.QueryResult {
	return &config.QueryResult{
		Timestamp: time.Now(),
		Files: []config.FileInfo{
			{
				Path:        "/tmp/test.txt",
				Exists:      true,
				IsDir:       false,
				Owner:       "user1",
				Group:       "group1",
				Permissions: "-rw-r--r--",
			},
			{
				Path:        "/tmp/dir",
				Exists:      true,
				IsDir:       true,
				Owner:       "user2",
				Group:       "group2",
				Permissions: "drwxr-xr-x",
			},
			{
				Path:   "/tmp/missing",
				Exists: false,
				Error:  "路径不存在",
			},
		},
		Summary: config.QuerySummary{
			TotalChecked: 3,
			TotalExists:  2,
			TotalMissing: 1,
			TotalDirs:    1,
			TotalFiles:   1,
		},
	}
}

// createTestCleanResult 创建测试清理结果
func createTestCleanResult() *config.CleanResult {
	return &config.CleanResult{
		Timestamp: time.Now(),
		Deleted: []config.FileInfo{
			{
				Path:   "/tmp/deleted1.txt",
				Exists: false,
				IsDir:  false,
			},
			{
				Path:   "/tmp/deleted2",
				Exists: false,
				IsDir:  true,
			},
		},
		Failed: []config.FileInfo{
			{
				Path:  "/tmp/failed.txt",
				Error: "权限不足",
			},
		},
		Skipped: []config.FileInfo{
			{
				Path:  "/tmp/skipped.txt",
				Error: "用户取消",
			},
		},
		Summary: config.CleanSummary{
			TotalChecked: 4,
			TotalDeleted: 2,
			TotalFailed:  1,
			TotalSkipped: 1,
		},
	}
}

// createTestProgramCheckResult 创建测试程序检查结果
func createTestProgramCheckResult() *config.ProgramCheckResult {
	return &config.ProgramCheckResult{
		Timestamp: time.Now(),
		Programs: []config.ProgramInfo{
			{
				Name:      "docker",
				Installed: true,
				Version:   "24.0.6",
				Path:      "/usr/bin/docker",
			},
			{
				Name:      "git",
				Installed: true,
				Version:   "2.39.2",
				Path:      "/usr/bin/git",
			},
			{
				Name:      "nonexistent",
				Installed: false,
				Error:     "程序未找到",
			},
		},
		Summary: config.ProgramCheckSummary{
			TotalChecked:   3,
			TotalInstalled: 2,
			TotalMissing:   1,
		},
	}
}

func TestNewFormatter(t *testing.T) {
	formatter := NewFormatter()

	if formatter == nil {
		t.Error("Expected Formatter instance, got nil")
	}
}

func TestFormatQueryText(t *testing.T) {
	formatter := NewFormatter()
	result := createTestQueryResult()

	output, err := formatter.FormatQuery(result, "text")

	assertNoError(t, err)
	assertContains(t, output, "file query result")
	assertContains(t, output, "/tmp/test.txt")
	assertContains(t, output, "/tmp/dir")
	assertContains(t, output, "/tmp/missing")
	assertContains(t, output, "summary")
	assertContains(t, output, "Total Checked: 3")
}

func TestFormatQueryJSON(t *testing.T) {
	formatter := NewFormatter()
	result := createTestQueryResult()

	output, err := formatter.FormatQuery(result, "json")

	assertNoError(t, err)
	assertContains(t, output, "timestamp")
	assertContains(t, output, "files")
	assertContains(t, output, "summary")
	assertValidJSON(t, output)

	// 验证具体内容
	assertContains(t, output, "/tmp/test.txt")
	assertContains(t, output, "user1")
	assertContains(t, output, "group1")
}

func TestFormatQueryCSV(t *testing.T) {
	formatter := NewFormatter()
	result := createTestQueryResult()

	output, err := formatter.FormatQuery(result, "csv")

	assertNoError(t, err)
	assertContains(t, output, "Path,Exists,Type,Owner,Group,Permissions,Error")
	assertContains(t, output, "/tmp/test.txt")
	assertContains(t, output, "user1")
	assertContains(t, output, "group1")
	assertValidCSV(t, output)
}

func TestFormatCleanText(t *testing.T) {
	formatter := NewFormatter()
	result := createTestCleanResult()

	output, err := formatter.FormatClean(result, "text")

	assertNoError(t, err)
	assertContains(t, output, "file clean result")
	assertContains(t, output, "already delete file")
	assertContains(t, output, "delete failed")
	assertContains(t, output, "skipped")
	assertContains(t, output, "summary")
	assertContains(t, output, "Total Deleted: 2")
}

func TestFormatCleanJSON(t *testing.T) {
	formatter := NewFormatter()
	result := createTestCleanResult()

	output, err := formatter.FormatClean(result, "json")

	assertNoError(t, err)
	assertContains(t, output, "timestamp")
	assertContains(t, output, "deleted")
	assertContains(t, output, "failed")
	assertContains(t, output, "skipped")
	assertContains(t, output, "summary")
	assertValidJSON(t, output)
}

func TestFormatCleanCSV(t *testing.T) {
	formatter := NewFormatter()
	result := createTestCleanResult()

	output, err := formatter.FormatClean(result, "csv")

	assertNoError(t, err)
	assertContains(t, output, "Path,Status,Type,Error")
	assertContains(t, output, "deleted")
	assertContains(t, output, "failed")
	assertContains(t, output, "skipped")
	assertValidCSV(t, output)
}

func TestFormatProgramCheckText(t *testing.T) {
	formatter := NewFormatter()
	result := createTestProgramCheckResult()

	output, err := formatter.FormatProgramCheck(result, "text")

	assertNoError(t, err)
	assertContains(t, output, "program check result")
	assertContains(t, output, "docker")
	assertContains(t, output, "git")
	assertContains(t, output, "nonexistent")
	assertContains(t, output, "summary")
	assertContains(t, output, "Total Installed: 2")
}

func TestFormatProgramCheckJSON(t *testing.T) {
	formatter := NewFormatter()
	result := createTestProgramCheckResult()

	output, err := formatter.FormatProgramCheck(result, "json")

	assertNoError(t, err)
	assertContains(t, output, "timestamp")
	assertContains(t, output, "programs")
	assertContains(t, output, "summary")
	assertContains(t, output, "docker")
	assertContains(t, output, "24.0.6")
	assertValidJSON(t, output)
}

func TestFormatProgramCheckCSV(t *testing.T) {
	formatter := NewFormatter()
	result := createTestProgramCheckResult()

	output, err := formatter.FormatProgramCheck(result, "csv")

	assertNoError(t, err)
	assertContains(t, output, "Program,Installed,Version,Path,Error")
	assertContains(t, output, "docker")
	assertContains(t, output, "24.0.6")
	assertContains(t, output, "nonexistent")
	assertValidCSV(t, output)
}

// assertContains 检查包含关系
func assertContains(t *testing.T, s, substr string) {
	t.Helper()
	if !strings.Contains(s, substr) {
		t.Fatalf("Expected to contain '%s', got: %s", substr, s)
	}
}

// assertNoError 检查没有错误
func assertNoError(t *testing.T, err error) {
	t.Helper()
	if err != nil {
		t.Fatalf("Expected no error, got: %v", err)
	}
}

// assertValidJSON 检查有效的 JSON
func assertValidJSON(t *testing.T, s string) {
	t.Helper()
	var js interface{}
	if err := json.Unmarshal([]byte(s), &js); err != nil {
		t.Fatalf("Invalid JSON: %s, error: %v", s, err)
	}
}

// assertValidCSV 检查有效的 CSV
func assertValidCSV(t *testing.T, s string) {
	t.Helper()
	reader := csv.NewReader(strings.NewReader(s))
	_, err := reader.ReadAll()
	if err != nil {
		t.Fatalf("Invalid CSV: %s, error: %v", s, err)
	}
}
